﻿//Created by Action Script Viewer - http://www.buraks.com/asv
package Engine.Classes {
    import flash.events.*;
    import Engine.Events.*;
    import Engine.Managers.*;
    import Engine.Interfaces.*;
    import flash.geom.*;
    import Engine.Helpers.*;
    import flash.utils.*;
    import __AS3__.vec.*;
    import com.greensock.*;
    import flash.system.*;
    import Engine.*;

	/**
	 * 维护所有子SavedObject的二维坐标关系
	 */
    public class World extends PositionedObjectContainer implements IEventDispatcher {

        private static const PERF_PROBABILITY:int = 10000;
        private static const PERF_UPDATE_INTERVAL:Number = 5000;
        private static const PERF_FPS:String = "fps";
        private static const PERF_MEM_USAGE:String = "mem_usage";
        protected static const DEFAULT_LAYER_NAME:String = "default";

        private static var m_instance:World = null;

        protected var m_tileMap:TileMap = null;
        protected var m_terrain:ITerrainMap;
        protected var m_dispatcher:EventDispatcher;
        protected var m_initialized:Boolean;
        protected var m_lastPerfUpdate:Number = 0;
        protected var m_perfFrameCount:int = 0;
        protected var m_perfTracking:Boolean = false;
        protected var m_errorOnNullObject:Boolean = false;
        protected var m_collisionMap:CollisionMap = null;
        protected var m_defaultLayer:ObjectLayer = null;
        protected var m_layerLookup:Dictionary;
        protected var m_objectCounts:Object;
        private var m_currentShard:int = 0;
        protected var m_currentNumShards:int = 2;
        private var m_maxNumShards:int = 16;
        private var m_lastShardTimeVector:Vector.<Number> = null;
        private var m_deltaShardTimeVector:Vector.<Number> = null;
        private var m_scrollTween:TweenLite;

        public function World(_arg1:int, _arg2:int){
            this.m_layerLookup = new Dictionary();
            this.m_objectCounts = new Object();
            super(_arg1, _arg2);
            this.m_dispatcher = new EventDispatcher(this);
            m_instance = this;
            FreezeManager.getInstance().addEventListener(FreezeEvent.THAWED, this.resetPerformanceTracking);
        }
        public static function getInstance():World{
            return (m_instance);
        }

        override public function loadObject(_arg1:Object):void{
            var _local3:WorldObject;
            LoadingManager.startWorldLoad();
            this.cleanUp();
            if (((_arg1.sizeX) && (_arg1.sizeY))){
                m_size.x = _arg1.sizeX;
                m_size.y = _arg1.sizeY;
            };
            this.initialize();
            this.m_defaultLayer = new ObjectLayer(DEFAULT_LAYER_NAME);
            this.addLayer(this.m_defaultLayer);
            var _local2:int;
            while (_local2 < _arg1.objects.length) {
                if (((("null" == _arg1.objects[_local2])) || ((null == _arg1.objects[_local2])))){
                    if (this.m_errorOnNullObject){
                        throw (new Error("object data was null"));
                    };
                    trace("Null object loaded");
                };
                _local3 = this.createObjectInstance(_arg1.objects[_local2]);
                if (_local3){
                    _local3.setOuter(this);
                    _local3.loadObject(_arg1.objects[_local2]);
                    _local3.attach();
                };
                _local2++;
            };
        }
        protected function createObjectInstance(_arg1:Object):WorldObject{
            return ((SavedObject.getInstance(_arg1.className) as WorldObject));
        }
        public function initialize():void{
			
            this.initShards();
            if (GlobalEngine.engineOptions.tileMapClass == null){
                GlobalEngine.engineOptions.tileMapClass = TileMap;
            };
            this.m_tileMap = (new GlobalEngine.engineOptions.tileMapClass(getGridWidth(), getGridHeight()) as TileMap);
            this.m_collisionMap = new CollisionMap();
            this.m_collisionMap.initialize(getGridWidth(), getGridHeight());
            this.m_initialized = true;
            GlobalEngine.viewport.clearBitmapData();
            GlobalEngine.viewport.regenerateBitmapData();
        }
        public function cleanUp():void{
            var _local3:IEngineObject;
            var _local1:Array = m_children.concat([]);
            var _local2:int;
            while (_local2 < _local1.length) {
                _local3 = (_local1[_local2] as IEngineObject);
                if (_local3 != null){
                    _local3.detach();
                    _local3.cleanUp();
                };
                _local2++;
            };
            m_children = [];
            this.m_layerLookup = new Dictionary();
            this.m_defaultLayer = null;
            this.m_tileMap = null;
            this.m_collisionMap.dispose();
            this.m_collisionMap = null;
            this.m_initialized = false;
        }
        public function getTileMap():TileMap{
            return (this.m_tileMap);
        }
        public function getRotation():int{
            return (Constants.ROTATION_0);
        }
        public function getReferencedAssets():Array{
            var _local3:WorldObject;
            var _local4:Array;
            var _local5:int;
            var _local1:Array = [];
            var _local2:int;
            while (_local2 < m_children.length) {
                _local3 = (m_children[_local2] as WorldObject);
                _local4 = _local3.getReferencedAssets();
                _local5 = 0;
                while (_local5 < _local4.length) {
                    if (_local1.indexOf(_local4[_local5]) == -1){
                        _local1.push(_local4[_local5]);
                    };
                    _local5++;
                };
                _local2++;
            };
            _local1 = _local1.concat(this.m_tileMap.getReferencedAssets());
            return (_local1);
        }
        public function getObjectById(_arg1:int, _arg2:int=16777215):WorldObject{
            var _local6:WorldObject;
            var _local3:WorldObject;
            var _local4:Array = getObjects();
            var _local5:int;
            while (_local5 < _local4.length) {
                _local6 = (_local4[_local5] as WorldObject);
                if ((((_local6.getId() == _arg1)) && (((_local6.getObjectType() & _arg2) > 0)))){
                    _local3 = _local6;
                    break;
                };
                _local5++;
            };
            return (_local3);
        }
        public function getObjectsByType(_arg1:int=16777215):Array{
            var _local5:WorldObject;
            var _local2:Array = [];
            var _local3:Array = getObjects();
            var _local4:int;
            while (_local4 < _local3.length) {
                _local5 = (_local3[_local4] as WorldObject);
                if ((_local5.getObjectType() & _arg1) > 0){
                    _local2.push(_local5);
                };
                _local4++;
            };
            return (_local2);
        }
        public function getObjectsByClass(_arg1:Class=null):Array{
            var _local5:WorldObject;
            var _local2:Array = new Array();
            var _local3:Array = getObjects();
            var _local4:int;
            while (_local4 < _local3.length) {
                _local5 = (_local3[_local4] as WorldObject);
                if (((!((_local5 == null))) && ((((_arg1 == null)) || ((_local5 is _arg1)))))){
                    _local2.push(_local5);
                };
                _local4++;
            };
            return (_local2);
        }
        override public function conditionallyRedrawAllObjects():void{
            var _local3:WorldObject;
            var _local1:Array = getObjects();
            var _local2:int;
            while (_local2 < _local1.length) {
                _local3 = (_local1[_local2] as WorldObject);
                if (_local3 != null){
                    _local3.conditionallyRedraw();
                };
                _local2++;
            };
        }
        public function updateWorld():void{
            var _local1:IEngineObject;
            var _local2:int;
            if (this.m_perfTracking){
                this.trackPerf();
            };
            if (this.m_initialized){
                this.updateShardTimers();
                _local2 = 0;
                while (_local2 < m_children.length) {
                    _local1 = (m_children[_local2] as IEngineObject);
                    if (_local1){
                        _local1.update();
                    };
                    _local2++;
                };
            };
        }
        public function updateCulling():void{
            var _local3:PositionedObject;
            var _local1:Array = getObjects();
            var _local2:int;
            while (_local2 < _local1.length) {
                _local3 = (_local1[_local2] as PositionedObject);
                if (_local3 != null){
                    _local3.updateCulling();
                };
                _local2++;
            };
        }
        public function addLayer(_arg1:ObjectLayer):void{
            var _local2:ObjectLayer = this.m_layerLookup[_arg1.name];
            if (_local2 == null){
                this.m_layerLookup[_arg1.name] = _arg1;
                m_children.push(_arg1);
                m_children.sortOn("priority", Array.NUMERIC);
                setObjectsDirty();
                _arg1.attach();
            };
        }
        public function removeLayer(_arg1:ObjectLayer):void{
            _arg1.cleanUp();
            delete this.m_layerLookup[_arg1.name];
            _arg1.detach();
        }
        public function getObjectLayerByName(_arg1:String):ObjectLayer{
            return (this.m_layerLookup[_arg1]);
        }
        public function insertObjectIntoDepthArray(_arg1:WorldObject, _arg2:String=null):void{
            if (_arg2 == null){
                _arg2 = DEFAULT_LAYER_NAME;
            };
            var _local3:ObjectLayer = this.getObjectLayerByName(_arg2);
            GlobalEngine.assert(!((_local3 == null)), ("No object layer found with name: " + _arg2));
            if (_local3 != null){
                _local3.insertObjectIntoDepthArray(_arg1);
                this.m_layerLookup[_arg1] = _local3;
            };
        }
        public function updateObjectInDepthArray(_arg1:WorldObject):void{
            var _local2:ObjectLayer = (this.m_layerLookup[_arg1] as ObjectLayer);
            if (_local2){
                _local2.updateObjectInDepthArray(_arg1);
            } else {
                if (_arg1.isAttached()){
                    this.insertObjectIntoDepthArray(_arg1);
                };
            };
        }
        public function removeObjectFromDepthArray(_arg1:WorldObject):void{
            var _local2:ObjectLayer = (this.m_layerLookup[_arg1] as ObjectLayer);
            if (_local2){
                delete this.m_layerLookup[_arg1];
                _local2.removeObjectFromDepthArray(_arg1);
            };
        }
        public function insertObjectIntoCollisionMap(_arg1:WorldObject):void{
            this.m_collisionMap.insertObject(_arg1);
        }
        public function removeObjectFromCollisionMap(_arg1:WorldObject):void{
            this.m_collisionMap.removeObject(_arg1);
        }
        protected function buildCollisionMap():void{
            this.m_collisionMap.initialize(getGridWidth(), getGridHeight());
        }
        public function showCollisionMap(_arg1:Boolean):void{
            if (this.m_collisionMap != null){
                this.m_collisionMap.showCollisionMap(_arg1);
            };
        }
        public function checkCollision(_arg1:int, _arg2:int, _arg3:int, _arg4:int, _arg5:Array, _arg6:Array=null, _arg7:int=16777215, _arg8:WorldObject=null):Boolean{
            var _local9:Boolean;
            var _local10:CollisionLookup = new CollisionLookup();
            _local10.init(_arg1, _arg2, _arg3, _arg4);
            _local10.ignoreObjects = _arg5;
            _local10.colliderTypes = _arg7;
            _local10.collidingObject = _arg8;
            _local10.colliders = _arg6;
            _local9 = this.m_collisionMap.checkCollision(_local10);
            return (_local9);
        }
        public function getCollisionMap():CollisionMap{
            return (this.m_collisionMap);
        }
        public function pickObject(_arg1:Point, _arg2:uint=16777215, _arg3:uint=0):PositionedObject{
            var _local4:int;
            var _local6:PositionedObject;
            var _local5:PositionedObject;
            _local4 = (m_children.length - 1);
            while (_local4 >= 0) {
                _local6 = (m_children[_local4] as PositionedObject);
                _local5 = (_local6.pickObject(_arg1, _arg2, _arg3) as PositionedObject);
                if (_local5 != null){
                    break;
                };
                _local4--;
            };
            return (_local5);
        }
        public function pickAllObjects(_arg1:Point, _arg2:PositionedObject=null, _arg3:uint=16777215, _arg4:uint=0):Array{
            var _local5:int;
            var _local6:PositionedObject;
            var _local8:PositionedObject;
            var _local7:Array = new Array();
            _local5 = (m_children.length - 1);
            while (_local5 >= 0) {
                _local8 = (m_children[_local5] as PositionedObject);
                _local6 = (_local8.pickObject(_arg1, _arg3, _arg4) as PositionedObject);
                if (_local6 != null){
                    if (_local6 === _arg2){
                        break;
                    };
                    _local7.push(_local6);
                };
                _local5--;
            };
            return (_local7);
        }
        public function get terrainMap():ITerrainMap{
            return (this.m_terrain);
        }
        public function startPerformanceTracking():void{
            if ((Math.random() * PERF_PROBABILITY) < 1){
                this.m_perfTracking = true;
            };
        }
        private function resetPerformanceTracking(_arg1:FreezeEvent):void{
            if (this.m_perfTracking == true){
                this.m_lastPerfUpdate = getTimer();
                this.m_perfFrameCount = 0;
            };
        }
        protected function trackPerf():void{
            var _local2:int;
            var _local3:int;
            var _local4:String;
            var _local5:Number;
            var _local6:String;
            var _local7:String;
            var _local1:Number = getTimer();
            this.m_perfFrameCount++;
            if (_local1 >= (this.m_lastPerfUpdate + PERF_UPDATE_INTERVAL)){
                _local2 = ((this.m_perfFrameCount / (_local1 - this.m_lastPerfUpdate)) * 1000);
                if (GlobalEngine){
                    _local3 = this.getNumObjects();
                    if (_local2 > 30){
                        _local4 = "30-60";
                    } else {
                        if (_local2 > 20){
                            _local4 = "20-30";
                        } else {
                            if (_local2 > 10){
                                _local4 = "10-20";
                            } else {
                                if (_local2 > 5){
                                    _local4 = "5-10";
                                } else {
                                    if (_local2 > 1){
                                        _local4 = "1-5";
                                    } else {
                                        _local4 = "0-1";
                                    };
                                };
                            };
                        };
                    };
                    _local5 = Number(((System.totalMemory / 0x0400) / 0x0400));
                    if (_local5 < 20){
                        _local6 = "0-20";
                    } else {
                        if (_local5 < 40){
                            _local6 = "20-40";
                        } else {
                            if (_local5 < 60){
                                _local6 = "40-60";
                            } else {
                                if (_local5 < 80){
                                    _local6 = "60-80";
                                } else {
                                    if (_local5 < 100){
                                        _local6 = "80-100";
                                    } else {
                                        if (_local5 < 120){
                                            _local6 = "100-120";
                                        } else {
                                            if (_local5 < 140){
                                                _local6 = "120-140";
                                            } else {
                                                if (_local5 < 160){
                                                    _local6 = "140-160";
                                                } else {
                                                    if (_local5 < 180){
                                                        _local6 = "160-180";
                                                    } else {
                                                        if (_local5 < 200){
                                                            _local6 = "180-200";
                                                        } else {
                                                            if (_local5 < 220){
                                                                _local6 = "200-220";
                                                            } else {
                                                                _local6 = "220-inf";
                                                            };
                                                        };
                                                    };
                                                };
                                            };
                                        };
                                    };
                                };
                            };
                        };
                    };
                    if (_local3 < 200){
                        _local7 = " 0To200 objects";
                    } else {
                        if (_local3 < 400){
                            _local7 = " 200To400 objects";
                        } else {
                            if (_local3 < 600){
                                _local7 = " 400To600 objects";
                            } else {
                                if (_local3 < 1000){
                                    _local7 = " 600To1000 objects";
                                } else {
                                    if (_local3 < 1500){
                                        _local7 = " 1000To1500 objects";
                                    } else {
                                        _local7 = " 1500ToInf objects";
                                    };
                                };
                            };
                        };
                    };
                    StatsManager.perf(PERF_FPS, "clientPerformance", "", "", "", "", _local2.toString());
                    StatsManager.perf(PERF_MEM_USAGE, "clientPerformance", "", "", "", "", _local3.toString());
                };
                this.m_lastPerfUpdate = _local1;
                this.m_perfFrameCount = 0;
            };
        }
        public function addEventListener(_arg1:String, _arg2:Function, _arg3:Boolean=false, _arg4:int=0, _arg5:Boolean=false):void{
            this.m_dispatcher.addEventListener(_arg1, _arg2, _arg3, _arg4);
        }
        public function dispatchEvent(_arg1:Event):Boolean{
            return (this.m_dispatcher.dispatchEvent(_arg1));
        }
        public function hasEventListener(_arg1:String):Boolean{
            return (this.m_dispatcher.hasEventListener(_arg1));
        }
        public function removeEventListener(_arg1:String, _arg2:Function, _arg3:Boolean=false):void{
            this.m_dispatcher.removeEventListener(_arg1, _arg2, _arg3);
        }
        public function willTrigger(_arg1:String):Boolean{
            return (this.m_dispatcher.willTrigger(_arg1));
        }
        public function set errorOnNullObject(_arg1:Boolean):void{
            this.m_errorOnNullObject = _arg1;
        }
        final private function initShards():void{
            this.m_maxNumShards = GlobalEngine.engineOptions.numShards;		// 这个值是16
			
			// 初始化两个shard时间数组，一个存储系统当前事件，另一个存储时间差
            this.m_lastShardTimeVector = new Vector.<Number>(this.m_maxNumShards, true);	
            this.m_deltaShardTimeVector = new Vector.<Number>(this.m_maxNumShards, true);
			
            var _local1:int;
            while (_local1 < this.m_maxNumShards) {
                this.m_lastShardTimeVector[_local1] = GlobalEngine.currentTime;
                this.m_deltaShardTimeVector[_local1] = 0;
                _local1++;
            };
        }
        final public function setNumShards(_arg1:int):void{
            if (_arg1 <= this.m_maxNumShards){
                this.m_currentNumShards = _arg1;
                if (this.m_currentShard > this.m_currentNumShards){
                    this.m_currentShard = 0;
                };
            };
        }
        final public function getRandomShard():int{
            return (Math.floor((Math.random() * this.m_maxNumShards)));
        }
        final public function getShardDelta(_arg1:int):Number{
            return ((((_arg1 % this.m_currentNumShards))!=this.m_currentShard) ? -1 : this.m_deltaShardTimeVector[_arg1]);
        }
		
		/**
		 * 这套逻辑实现了每个物品每秒只会被update一次 
		 */
        final private function updateShardTimers():void{
            this.m_currentShard = (++this.m_currentShard % this.m_currentNumShards);	// 这个值总是在0,1之间变化
            var _local1:int = this.m_currentShard;
            while (_local1 < this.m_maxNumShards) {
                if (this.m_lastShardTimeVector[_local1] > 0){
                    this.m_deltaShardTimeVector[_local1] = ((GlobalEngine.currentTime - this.m_lastShardTimeVector[_local1]) / 1000);
                };
                this.m_lastShardTimeVector[_local1] = GlobalEngine.currentTime;
                _local1 = (_local1 + this.m_currentNumShards);	
            };
        }
        public function incObjectCountMap(_arg1:String):void{
            if (this.m_objectCounts.hasOwnProperty(_arg1)){
                var _local2 = this.m_objectCounts;
                var _local3 = _arg1;
                var _local4 = (_local2[_local3] + 1);
                _local2[_local3] = _local4;
            } else {
                this.m_objectCounts[_arg1] = 1;
            };
        }
        public function decObjectCountMap(_arg1:String):void{
            if (this.m_objectCounts.hasOwnProperty(_arg1)){
                var _local2 = this.m_objectCounts;
                var _local3 = _arg1;
                var _local4 = (_local2[_local3] - 1);
                _local2[_local3] = _local4;
            } else {
                this.m_objectCounts[_arg1] = 0;
            };
        }
        public function getObjectCount(_arg1:String):int{
            if (this.m_objectCounts.hasOwnProperty(_arg1)){
                return (this.m_objectCounts[_arg1]);
            };
            return (0);
        }
        public function centerOnObject(_arg1:WorldObject, _arg2:Number=1):void{
            if (this.m_scrollTween){
                this.m_scrollTween.complete(true);
                this.m_scrollTween = null;
            };
            var _local3:Vector3 = _arg1.getPosition();
            var _local4:Object = {
                startPos:GlobalEngine.viewport.getScrollPosition(),
                alpha:0
            };
            var _local5:IsoViewport = (GlobalEngine.viewport as IsoViewport);
            _local5.centerOnTilePos(_local3.x, _local3.y);
            _local4.endPos = GlobalEngine.viewport.getScrollPosition();
            GlobalEngine.viewport.setScrollPosition(_local4.startPos);
            this.m_scrollTween = TweenLite.to(_local4, _arg2, {
                alpha:1,
                onUpdate:this.onUpdateScrollTween,
                onUpdateParams:[_local4],
                onComplete:this.onCompleteScrollTween
            });
        }
        protected function onUpdateScrollTween(_arg1:Object):void{
            var _local2:Vector2 = Vector2.lerp(_arg1.startPos, _arg1.endPos, _arg1.alpha);
            GlobalEngine.viewport.setScrollPosition(_local2);
        }
        protected function onCompleteScrollTween():void{
            this.m_scrollTween = null;
        }

    }
}//package Engine.Classes 
